package com.lastproject.ejb.dao;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.xml.bind.DatatypeConverter;

import com.lastproject.dto.UserDTO;
import com.lastproject.ejb.entities.User;
import com.lastproject.ejb.service.MailSender;
import com.lastproject.ejb.service.PasswordEncryption;
import com.lastproject.ejb.util.UserConversion;

/**
 * Session Bean implementation class UserDAO
 * Author: Alexandra
 */
@Stateless
public class UserDAO implements UserDAORemote {

	@PersistenceContext
	EntityManager entityManager;
	
	@EJB
	UserConversion conv;
	
    public UserDAO() {
    	
    }
    
    
//    @Interceptors(PermissionInterceptor.class)
//    @PermissionAnnotation(canCRUDOnEmployees=1)
    @Override
	public void create(UserDTO userDTO) {
    	
    	// initial password, represented as a string
    	String tempPass = userDTO.getPassword();
    	String password_encrypted2 = "";

    	System.out.println("pass from gui: " + tempPass);
    	
    	try {

    		// initial password after being hashed using SHA-1
    		// represented as byte[]
    		byte[] hashedPassword = new PasswordEncryption()
    			.getEncryptedPassword(tempPass, new PasswordEncryption().getSalt());
			
    		// hashed password stored as string
    		// this version of the password will be stored in the db
			password_encrypted2 = DatatypeConverter.printHexBinary(hashedPassword);
			userDTO.setPassword(password_encrypted2);
		} catch (NoSuchAlgorithmException | InvalidKeySpecException | UnsupportedEncodingException e) {
			e.printStackTrace();
		}
    	
		entityManager.persist(conv.fromDTO(userDTO));
		
		// send email with instructions to activate account
		sendInstructionsEmail("newAccount", userDTO.getWorkEmail(), 
				userDTO.getUsername(), password_encrypted2);
		
	}

	@Override
	public UserDTO findById(int id) {
		User user = entityManager.find(User.class, id);
		return conv.fromEntity(user);
	}

	@Override
	public List<UserDTO> getAll() {
		List<User> userList = entityManager.createQuery("Select u from User u",
				User.class).getResultList();
		
		List<UserDTO> usersDTO = new ArrayList<UserDTO>();

		for (User user : userList) {
			UserDTO uDTO = new UserDTO();
			uDTO = conv.fromEntity(user);
			usersDTO.add(uDTO);

		}

		return usersDTO;
	}

	@Override
	public void edit(UserDTO userDTO) {
		entityManager.merge(conv.fromDTO(userDTO));
	}

	@Override
	public void remove(UserDTO userDTO) {
		User user = entityManager.find(User.class, conv.fromDTO(userDTO)
				.getId());
		entityManager.remove(user);
	}
	
	public User findUserByCredentials(String username, String hashedPassword) {
		// find user by the credentials stored in the db			
		List<User> userList = entityManager.createQuery("Select u from User u "
				+ "where u.username = :username "
				+ "and u.password = :password "
				+ "and u.enabled = true",User.class)
				.setParameter("username", username)
				.setParameter("password", hashedPassword)
				.getResultList();
				
		if(userList.size() != 0) {
			return userList.get(0);
		} else 
			return null;
	}
	
	@Override
	public UserDTO findCreatorUserBySurvey(int idSurvey) {
		// find the creator of a survey
		List<User> userList = entityManager.createQuery("Select u from User u JOIN u.surveys s WHERE s.id = :id",User.class)
				.setParameter("id", idSurvey)
				.getResultList();
		
		
		UserDTO userDTO = conv.fromEntity(userList.get(0));
		return userDTO;

	}

	@Override
	public UserDTO userLogin(String logUsername, String logPassword) {
		// login user
		// can be accessed by anyone
		User logUser = null;
		UserDTO logUserDTO = null;
		byte[] hashedPasswordByte;
		String hashedPasswordString = "";
		
		PasswordEncryption passEncript = new PasswordEncryption();
				
		try {
			// hash password provided by user in GUI
			hashedPasswordByte = passEncript.getEncryptedPassword(logPassword, passEncript.getSalt());
			// convert hashed password in String
			hashedPasswordString = DatatypeConverter.printHexBinary(hashedPasswordByte);
		} catch (NoSuchAlgorithmException | InvalidKeySpecException
				| UnsupportedEncodingException e) {
			e.printStackTrace();
		} finally {
			// do nothing
		}
		
		if(findUserByCredentials(logUsername, hashedPasswordString) != null) {
			logUser = findUserByCredentials(logUsername, hashedPasswordString);
			logUserDTO = conv.fromEntity(logUser);
		}
		
		return logUserDTO;
	}
	
	@Override
	public UserDTO changePassword(String logUsername, String passToChange, String newPassword) {
		// can be accessed by anyone
		List<User> userList = entityManager.createQuery("SELECT u FROM User u "
				+ "WHERE u.username = :logUsername", User.class)
				.setParameter("logUsername", logUsername)
				.getResultList();
		
		String hashedNewPassword = "";
		
		if((userList.size() != 0) && (userList.get(0).getStatusPassword().equals("newPassword"))) {
			UserDTO userDTO = conv.fromEntity(userList.get(0));
			
			try {

	    		// new password after being hashed using SHA-1
	    		// represented as byte[]
	    		byte[] hashedPassword = new PasswordEncryption()
	    			.getEncryptedPassword(newPassword, new PasswordEncryption().getSalt());
				
	    		// hashed password stored as string
	    		// this version of the password will be stored in the db
	    		hashedNewPassword = DatatypeConverter.printHexBinary(hashedPassword);
				userDTO.setPassword(hashedNewPassword);
				
				// change password status from "newPassword"
				// to "changedPassword"
				userDTO.setStatusPassword("changePassword");
				userDTO.setEnabled(true);
				
				// update user info in db
				edit(userDTO);
			} catch (NoSuchAlgorithmException | InvalidKeySpecException | UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			
			return userDTO;
		} else
			return null;
	}
	
	@Override
	public boolean requestChangePassword(String providedEmail) {
		// can be accessed by anyone
		List<User> userList = entityManager.createQuery("SELECT u FROM User u "
				+ "WHERE u.workEmail = :providedEmail", User.class)
				.setParameter("providedEmail", providedEmail)
				.getResultList();
		
		int randNr = 1 + (int)(Math.random()*5); 
		String generatedTempPassword = "" + randNr;
		String password_encrypted2 = "";
		
		if(userList.size() != 0) {
			UserDTO userDTO = conv.fromEntity(userList.get(0));
			
			userDTO.setStatusPassword("newPassword");
			
			try {

	    		// initial password after being hashed using SHA-1
	    		// represented as byte[]
	    		byte[] hashedPassword = new PasswordEncryption()
	    			.getEncryptedPassword(generatedTempPassword, new PasswordEncryption().getSalt());
				
	    		// hashed password stored as string
	    		// this version of the password will be stored in the db
				password_encrypted2 = DatatypeConverter.printHexBinary(hashedPassword);
				userDTO.setPassword(password_encrypted2);
			} catch (NoSuchAlgorithmException | InvalidKeySpecException | UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			
			edit(userDTO);
			
			// send email with instructions to change password
			sendInstructionsEmail("changePw", userDTO.getWorkEmail(), 
					userDTO.getUsername(), password_encrypted2);
			
			return true;
		} else		
			return false;
	}
	
	@Override
	public UserDTO getLibrarian() {
		List<User> userList = entityManager.createQuery("SELECT u FROM User u "
				+ "JOIN u.roles r "
				+ "WHERE r.name = :roleName", User.class)
				.setParameter("roleName", "libraryAdmin")
				.getResultList();
		
		if(userList.size() != 0) {
			UserDTO librarian = conv.fromEntity(userList.get(0));
			return librarian;
		} else 
			return null;
	}
	
	public void sendInstructionsEmail(String emailPurpose, String receiverEmail, 
			String receiverUsername, String encryptPass) {
		MailSender ms = new MailSender();

		String emailSender = "no-reply@msg-systems.com";
		String emailSubject = "";
		String emailContent = "";
		
		// property file that contains ip address of server
		// on which project is run
		// at the moment - localhost
		Properties ipConfigFile = new Properties();		
		// property file is stored in META-INF folder
		InputStream myIS = getClass().getClassLoader().getResourceAsStream("META-INF/ipconfigs.properties"); 
		
		try {
			// open property file
			ipConfigFile.load(myIS);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// get value of "ip" property
		String ipAddress = ipConfigFile.getProperty("ip");
		String firstAccessLink = ipAddress + "LastProjectWeb/faces/changePassword.xhtml"
				+ "?logUsername=" + receiverUsername
				+ "&logPassword=" + encryptPass;
		
		switch(emailPurpose) {
		
		case "newAccount":
			emailSubject = "Your account was created";
			emailContent = "Click the link provided below and follow the instructions"
					+ " to activate your account.\n\n"
					+ firstAccessLink;
			break;
		case "changePw":
			emailSubject = "Pasword change request";
			emailContent = "We received a request to change your password.\n"
					+ "Click the link provided below and follow the instructions in order to "
					+ "access your account.\n\n"
					+ firstAccessLink;
			break;
		}	
		
		ms.sendMail(emailSender, receiverEmail, emailSubject, emailContent);
	}


	

	
	
}
